#ifndef _EKU_ULIB_H_
#define _EKU_ULIB_H_

#include <inc/error.h>
#include <inc/types.h>
#include <inc/syscall.h>
#include <inc/trap.h>
#include <inc/stdarg.h>
#include <inc/env.h>
#include <inc/exception.h>
#include <inc/x86.h>
#include <inc/memlayout.h>
#include <inc/mmu.h>
#include <inc/spinlock.h>
#include <inc/file.h>
#include <inc/ips.h>

#include <user/umem.h>
#include <user/pipe.h>
#include <user/file.h>
#include <user/rpc.h>
#include <user/svc.h>
#include <user/mutex.h>
#include <user/pci.h>

/* The following is defined in ulib.S */
uint32_t syscall0(uint32_t num);
uint32_t syscall1(uint32_t num, uint32_t a);
uint32_t syscall2(uint32_t num, uint32_t a, uint32_t b);
uint32_t syscall3(uint32_t num, uint32_t a, uint32_t b, uint32_t c);
uint32_t syscall4(uint32_t num, uint32_t a, uint32_t b, uint32_t c, uint32_t d);
uint32_t syscall5(uint32_t num, uint32_t a, uint32_t b, uint32_t c, uint32_t d, uint32_t e);

void delay(void);

extern struct env_t envs[];
extern ptab_entry_t vpt[];
extern pdir_entry_t vpd[];

__attribute__((noreturn)) void kcb_ret(uint32_t ret);

static inline uint32_t sc_monitor(void)
{ return syscall0(SC_MONITOR); }

static inline void sc_exit(void)
{ syscall0(SC_EXIT); }

/* alias of system call */
static inline uint32_t sc_dumb_fork(void)
{ return syscall0(SC_DUMB_FORK); }
static inline uint32_t sc_fork(void)
{ return syscall0(SC_FORK); }
static inline uint32_t sc_sfork(void)
{ return syscall0(SC_SFORK); }
static inline uint16_t sc_cur_env(void)
{ return (uint16_t)syscall0(SC_CUR_ENV); }

extern uint16_t self_idx;
uint16_t fork(void);

static inline int sleep(uint16_t idx, uint8_t *lock)
{ return syscall2(SC_SLEEP, idx, (uint32_t)lock); }

static inline int wakeup(uint16_t idx, uint8_t *lock)
{ return syscall2(SC_WAKEUP, idx, (uint32_t)lock); }

static inline uint32_t fix_addr(void *va)
{ return syscall1(SC_FIX_ADDR, (uint32_t)va); }

static inline void set_pfhandler(pfhandler_t handler)
{ syscall1(SC_SET_PFHANDLER, (uint32_t)handler); }

static inline uint32_t
kprintf(const char *fmt, ...)
{
     va_list va;
     va_start(va, fmt);
     syscall2(SC_KPRINTF, (uint32_t)fmt, (uint32_t)va);
     va_end(va);
}

static inline void wait(void) { syscall0(SC_WAIT); }
static inline void waitb(int status) { syscall1(SC_WAITB, status); }

static inline int alloc_page(void *va, uint32_t perm)
{ return syscall2(SC_ALLOC_PAGE, (uint32_t)va, perm); }

static inline uint32_t remove_map(void *va)
{ return syscall1(SC_REMOVE_MAP, (uint32_t)va); }

static inline uint32_t share_page(void *va)
{ return syscall1(SC_SHARE_PAGE, (uint32_t)va); }

static inline int shared_map(void *va, uint32_t pte)
{ return syscall2(SC_SHARED_MAP, (uint32_t)va, pte); }


/* initial procedure of user process */
int user_init(void);

/* not the spin version of lock */
void sl_lock(uint8_t *sl);

/* pfhandler */
extern char pfhandler_entry[];
int pfhandler(struct trapframe_t tf, void *addr, uint32_t err, uint32_t level);


/* iec */
static inline int
iec_send(uint16_t idx, uint32_t a, uint32_t b, uint32_t c, uint32_t d)
{ return syscall5(SC_IEC_SEND, idx, a, b, c, d); }

static inline int
iec_recv(uint16_t idx)
{ return syscall1(SC_IEC_RECV, idx); }

static inline int
iec_send_free(void)
{ return syscall0(SC_IEC_SEND_FREE); }

static inline int
set_user_service(uint32_t num, uint16_t idx)
{ return syscall2(SC_SET_USER_SERVICE, num, idx); }

static inline uint16_t
get_user_service(uint32_t num)
{ return syscall1(SC_GET_USER_SERVICE, num); }

static inline uint32_t
read_klog_pipe(void *buf, uint32_t n)
{ return syscall2(SC_READ_KLOG_PIPE, (uintptr_t)buf, n); }

static inline uint32_t
read_irq_pipe(uint32_t num, void *buf, uint32_t n)
{ return syscall3(SC_READ_IRQ_PIPE, num, (uintptr_t)buf, n); }

static inline uint32_t
wait_irq(uint32_t num)
{ return syscall1(SC_WAIT_IRQ, num); }

extern uint32_t service_callback;
extern struct file_t *image_file;
extern int initial_idx;

struct up_info_t
{
	 struct mutex_t lock;
	 // the env id of parent
	 uint16_t parent_id;
};

extern struct up_info_t up_info;

#endif /* _EKU_ULIB_H_ */
